﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HappyFrogXna.Level;
using HappyFrogXna.Roles;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using HappyFrogXna.ScreenSystem;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using HappyFrogXna.Miscellaneous;

namespace HappyFrogXna
{
    delegate void SetDraggingEntityDelegate(Entity e);
    delegate void EndDrawingDelegate();
    delegate void BeginDrawingDelegateWithCameraDelegate();
    delegate void BeginDrawingDelegateWithoutCameraDelegate();

    class EntityComponentSelector
    {
        SetDraggingEntityDelegate _setDraggingEntity;
        EndDrawingDelegate _endDrawing;
        BeginDrawingDelegateWithCameraDelegate _beginDrawingDelegateWithCamera;
        BeginDrawingDelegateWithoutCameraDelegate _beginDrawingDelegateWithoutCamera;

        Texture2D _topviewBg;

        EntityAssemble _allEntities;
        World _world;
        Camera2D _camera;

        const float _entityInterval = 1f;
        const float _horizonalMoveAcceleration = 0.5f;
        float _entityHorizonalExcursionX = 0;
        float _entityHorizonalBeginDragX = 0;
        float _horizonalMoveVelocity = 0;
        float _startHorizonalDragMoveTime;
        float _endHorizonalDragMoveTime;
        float _startHorizonalDragMoveX;
        float _endHorizonalDragMoveX;
        bool _beginHorizonalDragMove = false;
        bool _finishHorizonalDragMove = false;

        float _topViewVerticalScale = 1.5f;
        public float TopViewHeight { get; set; }

        string[] _frogList = { "Frog1" };
        string[] _objectList = { "RectWood1", "RectWood2", "RectWood3", "RectWood4", "RectWood5",
                               "RectIce1", "RectIce2", "RectIce3", "RectIce4", "RectIce5",
                               "RectStone1", "RectStone2", "RectStone3", "RectStone4", "RectStone5"};
        string[] _animalList = { "Animal1", "Animal2" };

        float _lastEntityInGalleryX = 2;
        private DragState _dragState = DragState.None;
        private Entity _entityDragged;
        private Vector2 _draggingMousePos;
        private Vector2 _beginDragPos;

        enum DragState
        { 
            None,
            BeginDragDown,
            IsDragging,
            BeginDragHorizontal,
            IsMoving
        }

        public EntityComponentSelector(World w, Camera2D c, 
            SetDraggingEntityDelegate setDraggingEntity, 
            BeginDrawingDelegateWithCameraDelegate beginDrawingDelegateWithCamera,
            BeginDrawingDelegateWithoutCameraDelegate beginDrawingDelegateWithoutCamera,
            EndDrawingDelegate endDrawing)
        {
            _world = w;
            _camera = c;
            _setDraggingEntity = setDraggingEntity;
            _beginDrawingDelegateWithCamera = beginDrawingDelegateWithCamera;
            _beginDrawingDelegateWithoutCamera = beginDrawingDelegateWithoutCamera;
            _endDrawing = endDrawing;
        }

        internal void LoadContent()
        {
            const float baseLineY = 2.5f;

            _topviewBg = GolbalXnaGadgetProvider.Instance.Content.Load<Texture2D>(@"Images\topviewbg");
            TopViewHeight = _topviewBg.Height * _topViewVerticalScale;

            _allEntities = new EntityAssemble();
            for (int i = 0; i < _frogList.Count<string>(); ++i)
            {
                _allEntities.Frogs.Add(EntityCreator.CreateEntity(_frogList[i], _world));
                float thisWidth = ConvertUnits.ToSimUnits(_allEntities.Frogs[i].Texture.Width) * _allEntities.Frogs[i].ScaleRadioOnComponentSelector;
                float thisPosX = _lastEntityInGalleryX + _entityInterval + thisWidth / 2.0f;
                _allEntities.Frogs[i].Position = new Vector2(thisPosX, baseLineY);
                _allEntities.Frogs[i].IsActive = false;
                _lastEntityInGalleryX = thisPosX + thisWidth;
            }
            for (int i = 0; i < _objectList.Count<string>(); ++i)
            {
                _allEntities.Objects.Add(EntityCreator.CreateEntity(_objectList[i], _world));
                float thisWidth = ConvertUnits.ToSimUnits(_allEntities.Objects[i].Texture.Width) * _allEntities.Objects[i].ScaleRadioOnComponentSelector;
                float thisPosX = _lastEntityInGalleryX + _entityInterval + thisWidth / 2.0f;
                _allEntities.Objects[i].Position = new Vector2(thisPosX, baseLineY);
                _allEntities.Objects[i].IsActive = false;
                _lastEntityInGalleryX = thisPosX + thisWidth;
            }
            for (int i = 0; i < _animalList.Count<string>(); ++i)
            {
                _allEntities.Animals.Add(EntityCreator.CreateEntity(_animalList[i], _world));
                float thisWidth = ConvertUnits.ToSimUnits(_allEntities.Animals[i].Texture.Width) * _allEntities.Animals[i].ScaleRadioOnComponentSelector;
                float thisPosX = _lastEntityInGalleryX + _entityInterval + thisWidth / 2.0f;
                _allEntities.Animals[i].Position = new Vector2(thisPosX, baseLineY);
                _allEntities.Animals[i].IsActive = false;
                _lastEntityInGalleryX = thisPosX + thisWidth;
            }
            _lastEntityInGalleryX += 2;
        }

        internal bool IsTouchedTopView(Vector2 position)
        {
            return ConvertUnits.ToDisplayUnits(position.Y) < TopViewHeight;
        }

        internal void HandleInput(InputHelper input)
        {
            Vector2 curPos = _camera.ConvertScreenToWorld(input.Cursor);

            Vector2 screenCenterPos = ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.ToVector2() / 2f);
            float cameraDaltaX = (_camera.ConvertScreenToWorld(_camera.Position).X - screenCenterPos.X) / 2; // I don't know why here need divide 2, but this works

            if (IsTouchedTopView(curPos))
            {
                if (input.IsNewMouseButtonPress(MouseButtons.LeftButton)) // Left button pressed
                {
                    _dragState = DragState.BeginDragHorizontal;
                    _entityHorizonalBeginDragX = curPos.X - _entityHorizonalExcursionX;
                    _horizonalMoveVelocity = 0;
                    _entityDragged = _allEntities.TestTouchedEntity(new Vector2(curPos.X - _entityHorizonalExcursionX - cameraDaltaX, curPos.Y));
                    if (_entityDragged != null)
                    {
                        _beginDragPos = new Vector2(_entityDragged.Body.Position.X + _entityHorizonalExcursionX + cameraDaltaX, _entityDragged.Body.Position.Y);
                        _dragState = DragState.BeginDragDown;
                    }
                    else
                    {
                        _beginDragPos = new Vector2(curPos.X, curPos.Y);
                    }
                }
                else if (input.IsNewMouseButtonRelease(MouseButtons.LeftButton)) // Left button released
                {                    
                    if (_dragState == DragState.IsMoving)
                    {
                        _finishHorizonalDragMove = true;
                        _endHorizonalDragMoveX = curPos.X;
                    }
                    _dragState = DragState.None;
                }
                else // Mouse moving
                {
                    switch (_dragState)
                    {
                        case DragState.BeginDragDown:
                            if (curPos.Y > _beginDragPos.Y + 1)
                            {
                                _draggingMousePos = curPos;
                                _dragState = DragState.IsDragging;
                            }
                            goto case DragState.BeginDragHorizontal; // move through
                        case DragState.BeginDragHorizontal:
                            if (Math.Abs(curPos.X - _beginDragPos.X) > 1)
                            {
                                _startHorizonalDragMoveX = curPos.X;
                                _beginHorizonalDragMove = true;
                                _dragState = DragState.IsMoving;
                            }
                            break;

                        case DragState.IsDragging:
                            _draggingMousePos = curPos;
                            break;

                        case DragState.IsMoving:
                            _entityHorizonalExcursionX = curPos.X - _entityHorizonalBeginDragX;
                            break;
                    }
                }
            }
            else
            {
                if (_dragState == DragState.IsDragging)
                {
                    Entity entity = _entityDragged.Clone();
                    entity.Position = curPos;
                    _setDraggingEntity(entity);
                    _dragState = DragState.None;
                }
                else if (_dragState == DragState.IsMoving)
                {
                    _finishHorizonalDragMove = true;
                    _endHorizonalDragMoveX = curPos.X;
                    _dragState = DragState.None;
                }
            }
        }

        internal void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            if (_beginHorizonalDragMove)
            {
                _beginHorizonalDragMove = false;
                _startHorizonalDragMoveTime = 1000 * gameTime.TotalGameTime.Seconds + gameTime.TotalGameTime.Milliseconds;
            }
            if (_finishHorizonalDragMove)
            {
                _finishHorizonalDragMove = false;
                if (gameTime.TotalGameTime.Seconds < (int)(_startHorizonalDragMoveTime / 1000))
                    _endHorizonalDragMoveTime = 1000 * (gameTime.TotalGameTime.Seconds + 60) + gameTime.TotalGameTime.Milliseconds;
                else
                    _endHorizonalDragMoveTime = 1000 * gameTime.TotalGameTime.Seconds + gameTime.TotalGameTime.Milliseconds;
                _horizonalMoveVelocity = (_endHorizonalDragMoveX - _startHorizonalDragMoveX) / (_endHorizonalDragMoveTime - _startHorizonalDragMoveTime) * 30;
            }
            if (Math.Abs(_horizonalMoveVelocity) > 1E-04)
            {
                _entityHorizonalExcursionX += _horizonalMoveVelocity;
                if (_horizonalMoveVelocity > 0)
                {
                    if (_horizonalMoveVelocity > _horizonalMoveAcceleration)
                        _horizonalMoveVelocity -= _horizonalMoveAcceleration;
                    else
                        _horizonalMoveVelocity = 0;
                }
                else
                { 
                    if (_horizonalMoveVelocity < -_horizonalMoveAcceleration)
                        _horizonalMoveVelocity += _horizonalMoveAcceleration;
                    else
                        _horizonalMoveVelocity = 0;
                }
            }
            if (_entityHorizonalExcursionX > 0)
            {
                _entityHorizonalExcursionX = 0;
                return;
            }
            else if (_entityHorizonalExcursionX < -_lastEntityInGalleryX + ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Width))
            {
                _entityHorizonalExcursionX = -_lastEntityInGalleryX + ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Width);
            }
        }

        internal void Draw(GameTime gameTime)
        {
            _endDrawing();
            _beginDrawingDelegateWithoutCamera();

            // Draw top view background
            GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_topviewBg, Vector2.Zero, null, Color.White, 0.0f, Vector2.Zero,
                new Vector2(ScreenInfo.ScreenSize.Width / _topviewBg.Width, _topViewVerticalScale), SpriteEffects.None, 0.7f);
            
            // Draw frogs
            for (int i = 0; i < _allEntities.Frogs.Count; ++i)
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_allEntities.Frogs[i].Texture, ConvertUnits.ToDisplayUnits(_allEntities.Frogs[i].Position.X + _entityHorizonalExcursionX, _allEntities.Frogs[i].Position.Y) , null,
                    Color.White, _allEntities.Frogs[i].Rotation, _allEntities.Frogs[i].EntitySprite.Origin, _allEntities.Frogs[i].ScaleRadioOnComponentSelector, SpriteEffects.None, 0.5f);
            }

            // Draw objects
            for (int i = 0; i < _allEntities.Objects.Count; ++i)
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_allEntities.Objects[i].Texture, ConvertUnits.ToDisplayUnits(_allEntities.Objects[i].Position.X + _entityHorizonalExcursionX, _allEntities.Objects[i].Position.Y), null,
                    Color.White, _allEntities.Objects[i].Rotation, _allEntities.Objects[i].EntitySprite.Origin, _allEntities.Objects[i].ScaleRadioOnComponentSelector, SpriteEffects.None, 0.5f);
            }

            // Draw animals
            for (int i = 0; i < _allEntities.Animals.Count; ++i)
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_allEntities.Animals[i].Texture, ConvertUnits.ToDisplayUnits(_allEntities.Animals[i].Position.X + _entityHorizonalExcursionX, _allEntities.Animals[i].Position.Y), null,
                    Color.White, _allEntities.Animals[i].Rotation, _allEntities.Animals[i].EntitySprite.Origin, _allEntities.Animals[i].ScaleRadioOnComponentSelector, SpriteEffects.None, 0.5f);
            }

            // Draw dragging entity
            if (_dragState == DragState.BeginDragDown)
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entityDragged.Texture, _camera.ConvertWorldToScreen(_beginDragPos), null,
                    Color.White, _entityDragged.Rotation, _entityDragged.EntitySprite.Origin, _entityDragged.ScaleRadioOnComponentSelector * 1.5f, SpriteEffects.None, 0.5f);
            }
            else if (_dragState == DragState.IsDragging)
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entityDragged.Texture, _camera.ConvertWorldToScreen(_draggingMousePos), null,
                    Color.White, _entityDragged.Rotation, _entityDragged.EntitySprite.Origin, _entityDragged.ScaleRadioOnComponentSelector * 1.5f, SpriteEffects.None, 0.5f);
            }

            _endDrawing();
            _beginDrawingDelegateWithCamera();
        }

    }
}
